_log('Yummy?');

function Y_onLoad() {
	_log("onload");
	
	enableDump(true);

	Yummy.init();
	
	_log("application initialized.");
}	

function enableDump( bool) {
	const PREFS_CID = "@mozilla.org/preferences;1";
	const PREFS_I_PREF = "nsIPref";
	const PREF_STRING = "browser.dom.window.dump.enabled";
	
	try {
		var Pref = new Components.Constructor(PREFS_CID,PREFS_I_PREF);
		var pref = new Pref();
		pref.SetBoolPref(PREF_STRING, bool);
	} catch(e) {
		_log(e); 
	}
}

var Pref = {
	preloadLinks: true,
	cacheTimeout: 30 * 60,
	cacheSize: 128,
	discardURIQueryPart: true,
	discardURIHashPart: true,
	
	
	load: function() {
	
	},
	
	save: function() {
	
	}
};


/**
*
*
*/
var Yummy = {
	badgeCache: new TimeoutCache(Pref.cacheTimeout, Pref.cacheSize), // 128: number of badges to store
	feedCache: new TimeoutCache(Pref.cacheTimeout, Pref.cacheSize),
	noTagsCache: [],
	unwantedTags: ["", "undefined"],
	tagbox: null,
	feedtree: null,
	taglabel: null,

	
	/**
	* Initialize
	*
	*/
	init: function() {
		// Attach listener for URI changes
		var mainWindow = window
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIWebNavigation)
			.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
			.rootTreeItem
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIDOMWindow);
	    
		var browser = mainWindow.getBrowser();
	
		browser.addProgressListener(LocationChangeListener,
	        Components.interfaces.nsIWebProgress.NOTIFY_LOCATION);
	
		// Save shortcuts to all XUL elements used
		Yummy.tagbox = document.getElementById("yummy-site-tags");
		Yummy.feedtree = document.getElementById("my-tree");
		Yummy.taglabel = document.getElementById("yummy-tags-for-label");
		
		// Fire event for current URL (when loaded via menu)
		var currentURL = content.document.URL;
		
		if (currentURL.indexOf("://") != -1) {
			var ioService = Components.classes['@mozilla.org/network/io-service;1']
   	   	.getService(Components.interfaces.nsIIOService);
			var uri = ioService.newURI(currentURL, null, null);
			
			Yummy.processNewURL(uri);
		}
	},	
	
	/**
	* Auxiliary method for getting and displaying the tag badge for a URI
	*
	*/
	getBadgeAndDisplay: function(uri) {
		Yummy.getBadge(uri, true);
	},
	
	/**
	*
	*
	*/
	getBadge: function(uri, display) {
		if (!uri || uri.spec.length == 0) {
			return;
		}
		
		_log("getBadge: " + uri.spec);
		
		// Handles JSON badge parsing
		function callback(badge) {
			var json = badge.evalJSON(true);
			var data = json[0];
			
			if (!data || !data.total_posts) {
				_log("no posts");
				
				Yummy.noTagsCache.push(uri.spec);
				
				return;
			}
			
			var tags = $H(data.top_tags);
			
			// Remove crap tags
			tags.remove.apply(tags, Yummy.unwantedTags);
			
			tags = Util.sortHashByValueReverse(tags);
			
			_log("got badge for: " + uri.spec);
			_log("cached: " + tags);
			
			Yummy.badgeCache.put(uri.spec, tags);
			
			if (!display) {
				return;
			}
			
			// Display tags
			Yummy.displayTagsForURI(uri);
			
			// Group any tags larger than 1000 occurances
			// We assume that there are del.icio.us posts tagged with every
			var pops = tags.keys().findAll(function(key) {
				return tags[key] > 1000;
			});
			
			// Select tag and fire event to get the feed
			if (pops.length > 0) {
				Yummy.taglabel.value = 
					"Showing links with tags: " + pops.join(", ");
					
				Yummy.tagbox.selectItemRange(
					Yummy.tagbox.getItemAtIndex(0),
					Yummy.tagbox.getItemAtIndex(pops.length - 1));
			} else {
				Yummy.taglabel.value = 
					"Showing links with tag: " + tags.keys().first();
					
				Yummy.tagbox.selectItem(Yummy.tagbox.getItemAtIndex(0));
			}
		}
		
		var md5_url = hex_md5(uri.spec);
		var badgeUrl = "http://badges.del.icio.us/feeds/json/url/data?hash="
			+ md5_url;
  
		Util.sendHttpRequest(badgeUrl, true, callback);
	},

	
	/**
	*/
	getFeed: function(feedUrl) {
		_log("retrieving feed: " + feedUrl);
		
		var source;
		
		// Try to retrieve data source from cache
		if (Yummy.feedCache.isCached(feedUrl)) {
			source = Yummy.feedCache.get(feedUrl);
		}

		if (!source || !source.loaded) {
			// Data source might be loaded but has not the .loaded property (yet)
		
			var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]
		  		.getService(Components.interfaces.nsIRDFService);
			  
			source = RDF.GetDataSource(feedUrl);
		}
		
		// Important for getting the .loaded property
		source = source.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
		
		// Not necessary since the <tree> is a sink in itself
		if (source.loaded || !Pref.preloadLinks) {
			Yummy.updateDataSource(source);
		} else {
			var sink = source.QueryInterface(Components.interfaces.nsIRDFXMLSink);
			
			var observer = {
				onBeginLoad: function(sink) {
				},
				
		      onInterrupt: function(sink) {
		      },
	
	   		onResume: function(sink) {
	   		},
	
		      onEndLoad: function(sink) {
		      	sink.removeXMLSinkObserver(observer);
		      	
					Yummy.feedCache.put(feedUrl, source);
		      	Yummy.updateDataSource(source);
				},
		
				onError: function(sink, status, errormsg) { 
					_log("RDF feed not loaded: " + errormsg);
				}
			};
				
			sink.addXMLSinkObserver(observer);
		}
	},
	
	/**
	* Replaces the data source associated with the feed tree
	*
	*/
	updateDataSource: function(ds) {
		var tree = document.getElementById("yummy-feed-tree");
			
		Util.replaceTreeDataSource(tree, ds);
		
		tree.setAttribute("ref", ds.URI.replace("\/rss", "").replace("\?plain", ""));
		tree.builder.rebuild();
	},
	
	/**
	* Cleans URL according to preferences
	*
	*/
	cleanURL: function(uri) {
		var i = -1;
	
		if (Pref.discardURIQueryPart) {
			i = uri.spec.indexOf("?");
		} else if (Pref.discardURIHashPart) {
			i = uri.spec.indexOf("#");
		}
			
		if (i != -1) {
			uri.spec = uri.spec.substring(0, i);
		}
		
		return uri;
	},
	
	processNewURL: function(uri) {
		uri = Yummy.cleanURL(uri);
	
		_log("processing: " + uri.spec);
		
		if (Yummy.badgeCache.isCached(uri.spec)) {
			Yummy.setStatusBarText(uri.spec + " [cached]");		
			Yummy.displayTagsForURI(uri);
			Yummy.tagbox.selectItem(Yummy.tagbox.getItemAtIndex(0));
		} else if (Yummy.noTagsCache.indexOf(uri.spec) < 0) {
			Yummy.setStatusBarText(uri.spec);
			Yummy.getBadgeAndDisplay(uri);
		} else {
			// No tags for this uri
			_log("No tags attached");
		}
	},
	
	displayTagsForURI: function(uri) {
		Yummy.clearTagsList();
		
		_log("getting the tags for: " + uri.spec);
		
		var tags = Yummy.badgeCache.get(uri.spec);
		
		if (!tags) {
			return;
		}
		
		tags.each(function(pair) {
			var item = document.createElement("listitem");
			item.setAttribute("value", pair.key);
			
			var cell1 = document.createElement("listcell");
			cell1.setAttribute("label", pair.value);
			
			var cell2 = document.createElement("listcell");
			cell2.setAttribute("label", pair.key);
			
			item.appendChild(cell1);
			item.appendChild(cell2);
			
			Yummy.tagbox.appendChild(item);
		});
	},
	
	clearTagsList: function() {
		Util.clearListbox(Yummy.tagbox);
	},
	
	tagSelected: function(ev) {
		ev.stopPropagation();
		
		var tagbox = document.getElementById("yummy-site-tags");
		var tags = [];
		
		tagbox.selectedItems.each(function(item) {
			tags.push(item.value);
		});
		
		if (tags.length == 0) {
			return;
		}
		
		Yummy.taglabel.value = 
			"Showing links with tags: " + tags.join(", ");
		
		Yummy.getFeed("http://del.icio.us/rss/tag/" + tags.join("%2B") + "?plain");
	},
	
	setStatusBarText: function(message) {	
		// document.getElementById("yummy-sidebar-label").value = message;
	},
  
  /**
  * Handle click on an URL in a <tree>. Mimics default browser behaviour.
  *
  * (Stolen from Firefox code - can't remember from where)
  */
  handleLinkClick: function(aEvent) {
		var tree = document.getElementById("yummy-feed-tree");
		var tbo = tree.treeBoxObject;	
		var row = { }, col = { }, obj = { };

		tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, col, obj);
  
		if (row.value == -1 || obj.value == "twisty") {
  			return;
  		}
  
		var modifKey = aEvent.shiftKey || aEvent.ctrlKey || aEvent.altKey || 
                 aEvent.metaKey  || aEvent.button == 1;

		if (aEvent.originalTarget.localName == "treechildren" && 
      	(aEvent.button == 0 || aEvent.button == 1)) {
			tbo.view.selection.select(row.value);

			var count = tree.view.selection.count;
		
		 	if (count != 1) {
	   		return;
			}
		
	  		var currentIndex = tree.currentIndex;			
			var builder = tree.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
 			var url = builder.getResourceAtIndex(currentIndex).ValueUTF8;
    
			openUILink(url, aEvent);
  		}
	}
}; // ! Yummy


var Util = {
	clearListbox: function(listbox) {
		if (!listbox.listBoxObject) {
			return;
		}
	
		var nrows = listbox.getRowCount();
		
		if (nrows == 0) {
			return;
		}
		
		for (var i = nrows - 1; i >= 0; --i) {
			listbox.removeItemAt(i);
		}
	},
	
	/**
	*
	*
	*/
	removeTreeDataSources: function(tree) {
		with (tree.database) {
			var it = GetDataSources();
    
			while (it.hasMoreElements()) {
				RemoveDataSource(it.getNext());
	    	}
    	}
  	},
  
  /**
  * Removes all currently attached data sources in a tree and adds another one
  *
  */
  replaceTreeDataSource: function(tree, ds) {
  		Util.removeTreeDataSources(tree);
		
		tree.database.AddDataSource(ds);
	},
  
  sortHashByValueReverse: function(h) {
		var sortedKeys = h.keys().sort(function(a, b) {
			return Util.compare(h[a], h[b], true);
		});
		
		var s = $H();
		
		sortedKeys.each(function(key) {
			s[key] = h[key];
		});
		
		return s;
	},
	
	compare: function(a, b, rev) {
		return !rev ? a - b : b - a;
	},

	sendHttpRequest: function(url, async, callback, throttleCallback) {
		with (new XMLHttpRequest()) {
			onreadystatechange = function() {
				if (readyState == 4) {
					switch (status) {
					case 200:
						if (callback && responseText) {
							callback(responseText);
						}
						break;
						
					case 503:
						if (throttleCallback) {
							throttleCallback();
						}
						break;
					}
				}
			};
			open("GET", url, async);
			// setRequestHeader('User-Agent', YUMMY_VERSION);
			send(null);
		}
	}
};

// Y_onLoad();